Adding Custom Applications and Libraries in the Yocto Environment

Overview

This document guides users in adding custom applications and library files (using "helloworld" as an example) to the Yocto build system. Through the following steps, you can integrate your code into the Yocto project and generate installation packages and libraries for use.

Table of Contents

  1. Environment Setup
  2. Adding a Custom Application
  3. Adding a Third-party Library

Environment Setup

Installation and Setup

  • Install necessary supporting software packages.
  • Clone the code.

For specific environment preparation, refer to: Development Environment Setup

Creating Project Directories

  • Set up directory structures for better management and development.
meta-custom/
├── conf/
│   └── layer.conf
├── recipes-apps/
│   └── helloworld/
│       ├── files/
│       │   ├── helloworld.c
│       │   ├── Makefile
│       │   └── helloworld.service
│       └── helloworld_1.0.bb
└── recipes-libs/
    └── lib-helloworld/
        ├── files/
        │   ├── lib_helloworld.c
        │   ├── lib_helloworld.h
        │   └── Makefile
        └── lib-helloworld_1.0.bb

Adding a Custom Application

Creating Application Files

Creating Layer Directories

mkdir -p ${YOCTO_DIR}/layers/meta-custom/recipes-apps/helloworld/files
mkdir -p ${YOCTO_DIR}/layers/meta-custom/conf

Configuring Layer

  • Edit the layers/meta-custom/conf/layer.conf file:
BBPATH .= ":${LAYERDIR}"
BBFILES += "${LAYERDIR}/recipes-*/*/*.bb"
LAYERDEPENDS_meta-custom = "core"
LAYERSERIES_COMPAT_meta-custom = "kirkstone"
  • Add the following content to layers/meta-qcom-distro/conf/bblayers.conf:
BBLAYERS = "
      ${WORKSPACE}/layers/meta-custom \
"
  • Additionally, add the following content to layers/meta-quectel/recipes-products/images/qcom-multimedia-image.bbappend:
IMAGE_INSTALL:append = "helloworld"
IMAGE_INSTALL:append = " \
        lib-helloworld \
        lib-helloworld-dev \
        lib-helloworld-staticdev \
"

Creating Application Source Code

  • Create helloworld.c in the meta-custom/recipes-apps/helloworld/files directory:
// helloworld.c
#include <stdio.h>

int main() {
    printf("Hello, world!\n");
    return 0;
}

Writing the Makefile

  • Create a Makefile in the meta-custom/recipes-apps/helloworld/files directory:
# Using compilation flags provided by Yocto
all: helloworld

helloworld: helloworld.c
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<

clean:
rm -f helloworld

.PHONY: all clean

Creating the Systemd Service File

  • Create helloworld.service in the meta-custom/recipes-apps/helloworld/files directory:
[Unit]
Description=Hello World Service

[Service]
Type=simple
ExecStart=/usr/bin/helloworld

[Install]
WantedBy=multi-user.target

Writing the BitBake Recipe

  • Create helloworld_1.0.bb in the meta-custom/recipes-apps/helloworld/ directory:
SUMMARY = "Hello World application"
DESCRIPTION = "A simple Hello World application."
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"

# List of source code files (including helloworld.c, Makefile, and systemd service file)
SRC_URI = "file://helloworld.c \
           file://Makefile \
           file://helloworld.service"

# Set the working directory (${WORKDIR} is the BitBake build directory)
S = "${WORKDIR}"

# Inherit the systemd class (add support for systemd services)
inherit systemd

# Define the systemd service file name (${PN} automatically changes to the package name)
SYSTEMD_SERVICE:${PN} = "helloworld.service"

# Configuration stage (skip configuration here)
do_configure() {
    bbnote skip do_configure  # Log skipping the configuration stage
}

# Compilation stage
do_compile() {
    # First execute make clean to clean up
    oe_runmake -f ${S}/Makefile clean
    # Then execute make to compile
    oe_runmake -f ${S}/Makefile
}

# Installation stage
do_install() {
    # Create installation directories (${D} is the target root directory, ${bindir} is typically /usr/bin)
    install -d ${D}${bindir}
    # Install the helloworld executable to /usr/bin with permission 755
    install -m 0755 ${S}/helloworld ${D}${bindir}

    # Create systemd service directories (${systemd_system_unitdir} is typically /lib/systemd/system)
    install -d ${D}${systemd_system_unitdir}
    # Install the helloworld.service service file with permission 644
    install -m 0644 ${S}/helloworld.service ${D}${systemd_system_unitdir}
}

Building and Running the Application

Setting Up the Build Environment

  • Enter the build environment code directory and configure the build environment:
source quectel_build/compile/build.sh
  • Users can choose to install applications based on their needs, using single compilation or rootfs compilation.

Installing the Application (Single Compilation, IPK Installation)

  • Execute the following command in the meta-custom/recipes-apps/ directory:
bitbake helloworld
  • After building, the generated .ipk file is located at:
find ~/build-qcom-wayland/tmp-glibc/deploy -name "helloworld*.ipk"
/home/quectel/build-qcom-wayland/tmp-glibc/deploy/ipk/armv8-2a/helloworld_1.0-r0_armv8-2a.ipk
  • Push the generated .ipk file to the device:
adb push .\helloworld_1.0-r0_armv8-2a.ipk /mnt
  • Install the .ipk file:
# Remount the filesystem in read-write mode before installation
mount -o remount,rw /usr
root@qcm6490-idp:/mnt# opkg install helloworld_1.0-r0_armv8-2a.ipk
  • Verify the installation:
root@qcm6490-idp:/mnt# /usr/bin/helloworld
Hello, world!

Installing the Application (Rootfs Compilation)

  • Execute the following commands in the meta-custom/recipes-apps/ directory:
# Customize by inputting "ProjectName", "ProjectRev", "CustName" as needed. Select valid fields from the prompts "Valid Projects" and "Valid CUST_NAME".
buildconfig QSM565DWF SG565DWFPARL1A01_BL01BP01K0M01_QDP_LP6.6.0XX.01.00X_V0X STD
# Execute buildall after the above command completes
buildall
# Execute buildpackage after the above command completes
buildpackage
root@qcm6490-idp:~# /usr/bin/helloworld
Hello, world!

Adding a Third-party Library

Creating Library Files

reating Layer Directories

mkdir -p ${YOCTO_DIR}/layers/meta-custom/recipes-libs/lib-helloworld/files

Creating Library Source Code

  • Create lib_helloworld.c and lib_helloworld.h in the meta-custom/recipes-libs/lib-helloworld/files/ directory:
// lib_helloworld.c
#include <stdio.h>

void hello_print(const char* name) {
    printf("[LIB] Hello, %s!\n", name);
}
// lib_helloworld.h
#ifndef LIB_HELLO_H
#define LIB_HELLO_H

void hello_print(const char*);

#endif

Writing the Makefile

  • Create a Makefile in the meta-custom/recipes-libs/lib-helloworld/files directory:
LIB_VERSION = "1.0"

all:
    ${CC} ${CFLAGS} -fPIC -c lib_helloworld.c -o lib_helloworld.o
    ${CC} ${CFLAGS} -fPIC -shared lib_helloworld.o -o libhello.so.${LIB_VERSION} \
        -Wl,-soname,libhello.so.1 ${LDFLAGS}
    ar rcs libhello.a lib_helloworld.o
    ln -sf libhello.so.${LIB_VERSION} libhello.so
    ln -sf libhello.so.${LIB_VERSION} libhello.so.1

clean:
    rm -f libhello.so*
    rm -f lib_helloworld.o
    rm -f libhello.a

Writing the BitBake Recipe

  • Create lib-helloworld_1.0.bb in the meta-custom/recipes-libs/lib-helloworld/ directory:
SUMMARY = "Hello World Library"
SECTION = "libs"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"

SRC_URI = "file://lib_helloworld.c \
           file://Makefile \
           file://lib_helloworld.h"

S = "${WORKDIR}"

EXTRA_OEMAKE = "'CC=${CC}' 'CFLAGS=${CFLAGS} -fPIC' 'LDFLAGS=${LDFLAGS}'"

do_install() {
    # 1. Create target directories
    install -d ${D}${libdir}

    # 2. Install shared library and create symbolic links
    install -m 0755 ${S}/libhello.so.${PV} ${D}${libdir}/
    ln -sf libhello.so.${PV} ${D}${libdir}/libhello.so
    ln -sf libhello.so.${PV} ${D}${libdir}/libhello.so.1

    # 3. Install static library
    install -m 0644 ${S}/libhello.a ${D}${libdir}/
}

# Package file distribution
FILES:${PN} = " \
    ${libdir}/libhello.so.1 \
    ${libdir}/libhello.so.${PV} \
"

FILES:${PN}-dev = " \
    ${libdir}/libhello.so \
"

FILES:${PN}-staticdev = " \
    ${libdir}/libhello.a \
"

Building and Using the Library

Setting Up the Build Environment

  • Enter the build environment code directory and configure the build environment:
source quectel_build/compile/build.sh

Using the Third-party Library (Single Compilation with BitBake)

  • Build using bitbake:
bitbake lib-helloworld
  • Search for the generated dynamic library files at the root directory:
find ./* -name "libhello*"
  • Push the library files to the device:
adb push libhello.so libhello.so.1 libhello.so.1.0 libhello.a /usr/lib
  • Fix execution permissions and recreate symbolic links:
chmod +x /usr/lib/libhello.so /usr/lib/libhello.so.1 /usr/lib/libhello.so.1.0 /usr/lib/libhello.a
ln -sf libhello.so.1.0 libhello.so.1
ln -sf libhello.so.1 libhello.so
  • Write a test program:
// static_test_helloworld.c
#include <stdio.h>

void hello_print(const char*);

int main() {
    printf("static lib test\n");
    hello_print("world");
    return 0;
}

// dynamic_test_helloworld.c
#include <stdio.h>

void hello_print(const char*);

int main() {
    printf("dynamic lib test\n");
    hello_print("world");
    return 0;
}
  • Compile and run the test program:
# Static library test
gcc static_test_helloworld.c -lhello -static -o static_hello
./static_hello
# Output:
static lib test
[LIB] Hello, world!

# Dynamic library test
gcc dynamic_test_helloworld.c /usr/lib/libhello.so -o dynamic_hello
./dynamic_hello
# Output:
dynamic lib test
[LIB] Hello, world!

Using the Third-party Library (Rootfs Compilation)

  • The compilation and burning process can be referenced from the method described above (Installing Applications with Rootfs Compilation).

  • Verify the usage (requires writing test programs as referenced above Using the Third-party Library (Single Compilation with BitBake)):

/mnt# ls -l /usr/lib/libhello*
-rw-r--r--. 2 root root 5002 Jan  1  1970 /usr/lib/libhello.a
lrwxrwxrwx. 1 root root   15 Jul 25 06:26 /usr/lib/libhello.so -> libhello.so.1.0
lrwxrwxrwx. 1 root root   15 Jul 25 06:26 /usr/lib/libhello.so.1 -> libhello.so.1.0
-rwxr-xr-x. 2 root root 5968 Jan  1  1970 /usr/lib/libhello.so.1.0

# Static library test
gcc static_test_helloworld.c -lhello -static -o static_hello
./static_hello
# Output:
static lib test
[LIB] Hello, world!

# Dynamic library test
gcc dynamic_test_helloworld.c /usr/lib/libhello.so -o dynamic_hello
./dynamic_hello
# Output:
dynamic lib test
[LIB] Hello, world!

Conclusion

Through the guidance provided in this document, users can successfully add custom applications and libraries in the Yocto environment, achieving personalized functional integration.